package middleware

import (
	"gin-web/app/dao"
	"gin-web/library/global"
	"gin-web/library/utils"
	"log"
	"time"

	jwt "github.com/appleboy/gin-jwt/v2"
	"github.com/gin-gonic/gin"
)

type login struct {
	Username string `form:"username" json:"username" binding:"required"`
	Password string `form:"password" json:"password" binding:"required"`
}

var identityKey = "id"

func helloHandler(c *gin.Context) {
	claims := jwt.ExtractClaims(c)
	user, _ := c.Get(identityKey)
	c.JSON(200, gin.H{
		"userID":   claims[identityKey],
		"userName": user.(*User).UserName,
		"text":     "Hello World.",
	})
}

// User demo
type User struct {
	UserName  string
	FirstName string
	LastName  string
}

var JwtMiddleware *jwt.GinJWTMiddleware

func init() {
	authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
		Realm:           "test zone",
		Key:             []byte("secret key"),
		Timeout:         time.Hour,
		MaxRefresh:      time.Hour,
		IdentityKey:     identityKey,
		PayloadFunc:     PayloadFunc,
		IdentityHandler: IdentityHandler,
		Authenticator:   Authenticator,
		LoginResponse:   LoginResponse,
		Unauthorized:    Unauthorized,
		// TokenLookup is a string in the form of "<source>:<name>" that is used
		// to extract token from the request.
		// Optional. Default value "header:Authorization".
		// Possible values:
		// - "header:<name>"
		// - "query:<name>"
		// - "cookie:<name>"
		// - "param:<name>"
		TokenLookup: "header: Authorization, query: token, cookie: jwt",
		// TokenLookup: "query:token",
		// TokenLookup: "cookie:token",

		// TokenHeadName is a string in the header. Default value is "Bearer"
		TokenHeadName: "Bearer",

		// TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens.
		TimeFunc: time.Now,
	})

	if err != nil {
		log.Fatal("JWT Error:" + err.Error())
	}
	JwtMiddleware = authMiddleware
}

// PayloadFunc data from mw.Authenticator(c)
func PayloadFunc(data interface{}) jwt.MapClaims {
	if v, ok := data.(*User); ok {
		return jwt.MapClaims{
			identityKey: v.UserName,
		}
	}
	return jwt.MapClaims{}
}

// IdentityHandler sets the identity for JWT.
/*
identity := mw.IdentityHandler(c)
	if identity != nil {
		c.Set(mw.IdentityKey, identity)
	}
*/
func IdentityHandler(c *gin.Context) interface{} {
	claims := jwt.ExtractClaims(c)
	workcode := claims[identityKey].(string)
	sessionUser, _ := dao.User.FindOneByWorkcode(workcode)
	c.Set("loginUser", sessionUser)
	return workcode
}

// Unauthorized is used to define customized Unauthorized callback function.
func Unauthorized(c *gin.Context, code int, message string) {
	global.FailWithDetailed(c, code, nil, message)
}

func LoginResponse(c *gin.Context, code int, token string, expire time.Time) {
	data := map[string]string{
		"token":  token,
		"expire": expire.Format(time.RFC3339),
	}
	global.OkWithData(c, data)
}

// Authenticator is used to validate login parameters.
// It must return user data as user identifier, it will be stored in Claim Array.
// Check error (e) to determine the appropriate error message.
func Authenticator(c *gin.Context) (interface{}, error) {
	var loginVal login
	if err := c.ShouldBind(&loginVal); err != nil {
		return "", jwt.ErrMissingLoginValues
	}
	name := loginVal.Username
	password := utils.Md5Str([]byte(loginVal.Password))
	user, err := dao.User.FindOneByUserAndPwd(name, password)
	if err != nil {
		return "", jwt.ErrMissingLoginValues
	}

	return user, nil
}
